/*******************************************************************************
 * Copyright (c) 2011 - 2012 TXT e-solutions SpA
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * This work was performed within the IoT_at_Work Project
 * and partially funded by the European Commission's
 * 7th Framework Programme under the research area ICT-2009.1.3
 * Internet of Things and enterprise environments.
 *
 *
 * Authors:
 *     Cristoforo Seccia (TXT e-solutions SpA)
 *     Salvatore Piccione (TXT e-solutions SpA)
 *
 * Contributors:
 *      Domenico Rotondi (TXT e-solutions SpA)
 *******************************************************************************/
package it.txt.access.capability.revocation.wizard.panel.controller;

import it.txt.access.capability.commons.filter.FilterXML;
import it.txt.access.capability.commons.schema.CapabilityNamespacesMapper;
import it.txt.access.capability.commons.signer.X509DocumentSigner;
import it.txt.access.capability.commons.utils.XMLPrinter;
import it.txt.access.capability.factory.CapabilitySchemaFactory;
import it.txt.access.capability.revocation.CapabilityRevocationType;
import it.txt.access.capability.revocation.wizard.Wizard;
import it.txt.access.capability.revocation.wizard.WizardPanelController;
import it.txt.access.capability.revocation.wizard.panel.PanelAssertion;
import it.txt.access.capability.revocation.wizard.panel.exception.PanelAssertionException;
import it.txt.access.capability.revocation.wizard.panel.objects.PanelAssertionObject;

import java.awt.Component;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JOptionPane;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;

/**
 *
 * @author Cristoforo Seccia (TXT e-solutions SpA)
 * @author Salvatore Piccione (TXT e-solutions SpA - salvatore.piccione AT network.txtgroup.com)
 */
public class ControllerPanelAssertion extends WizardPanelController {

    private static final Logger logger = Logger.getLogger(ControllerPanelAssertion.class.getName());
    private Document auCapRevXMLDoc = null;
    private URI auCapRevXMLDocURI = null;
    private PanelAssertion panelAssertion;
    private final int completedPackage;
    private boolean fileSaved;

    public ControllerPanelAssertion(Wizard wizard, int completedPercentage, String title) {
        super(wizard);
        panelAssertion = new PanelAssertion(title, getWizard(), this);
        this.completedPackage = completedPercentage;
        fileSaved = false;
    }

    @Override
    public Component getPanelComponent() {
        return panelAssertion;
    }

    @Override
    public boolean aboutToDisplayPanel() {
        try {
            recoverPaneAssertionObject(getBackwardObject());
            getWizard().setBackButtonEnabled(false);
            getWizard().setNextButtonEnabled(false);
            getWizard().setFinishButtonEnabled(false);
            getWizard().setCancelButtonEnabled(true);
            
            getWizard().setAccessRightsCreationPercentage(completedPackage);
            return true;
        } catch (PanelAssertionException ex) {
            JOptionPane.showMessageDialog(getWizard(), ex.getMessage(),
                    "Error", JOptionPane.ERROR_MESSAGE);
        }
        return false;
    }

    @Override
    public void displayingPanel() {

        Thread t = new Thread() {

            @Override
            public void run() {

                try {
                    // Recover all the needed objects.
                    PanelAssertionObject pao = (PanelAssertionObject) getBackwardObject();
                    panelAssertion.setProgressValue(0);
                    ResourceBundle progressBarMsgs = ResourceBundle.getBundle("bundles/PanelAssertion");
                    // Creating unsigned capability revocation
                    panelAssertion.setProgressText(progressBarMsgs.getString("progressBar1"));
                    
                    //TODO DEFINE REVOCATION FACTORY
                    JAXBContext context = JAXBContext.newInstance(CapabilityRevocationType.class);
                    
                    Marshaller marshaller = context.createMarshaller();
                    DocumentBuilderFactory xmlDocBuilderFactory = DocumentBuilderFactory.newInstance();
                    xmlDocBuilderFactory.setNamespaceAware(true);
                    xmlDocBuilderFactory.setIgnoringElementContentWhitespace(true);
                    DocumentBuilder xmlDocBuilder = xmlDocBuilderFactory.newDocumentBuilder();
                    auCapRevXMLDoc = xmlDocBuilder.newDocument();
                    marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.FALSE);
                    marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new CapabilityNamespacesMapper());
                    marshaller.marshal(pao.getRevocationType(),auCapRevXMLDoc);
                    
                    panelAssertion.setProgressText(progressBarMsgs.getString("progressBar2"));
                    panelAssertion.setProgressValue(25);
                    XMLPrinter.logDocumentElement(auCapRevXMLDoc.getDocumentElement(),
                            "Unsigned Capability Revocation token", false);
                    Thread.sleep(1000);

                    //Signing capability revocation
                    panelAssertion.setProgressText(progressBarMsgs.getString("progressBar3"));
                    X509DocumentSigner.signXMLElement(auCapRevXMLDoc.getDocumentElement(), pao.getKeyValues(),
                            CapabilitySchemaFactory.SIGN_DOCUMENT_AFTER_NODE);
                    panelAssertion.setProgressText(progressBarMsgs.getString("progressBar4"));
                    panelAssertion.setProgressValue(50);
                    Thread.sleep(1000);
                    
                    //Verifying digital signature
                    panelAssertion.setProgressText(progressBarMsgs.getString("progressBar5"));
                    if (X509DocumentSigner.verifyXMLElementSign(auCapRevXMLDoc.getDocumentElement())) {
                        logger.log(Level.INFO, "Signature verified!");
                        panelAssertion.setProgressText(progressBarMsgs.getString("progressBar6"));
                        panelAssertion.setProgressValue(75);

                        Thread.sleep(1000);
                        panelAssertion.setProgressText(progressBarMsgs.getString("progressBar7"));
                        panelAssertion.setProgressValue(100);

                    } else {
                        logger.log(Level.SEVERE, "Bad signature!");
                    }
                    getWizard().setAccessRightsCreationPercentage(100);
                    getWizard().setFinishButtonEnabled(true);
                    getWizard().setCancelButtonEnabled(false);
                    
                    //DEBUG
//                    String inMemorySer = XMLPrinter.printDocumentElement(auCapRevXMLDoc.getDocumentElement(), false);
//                    System.out.println("Signed token\n" + inMemorySer);
//                    //Verify the signature of the in-memory serialized file
//                    if (X509DocumentSigner.verifyXMLElementSign(xmlDocBuilderFactory.newDocumentBuilder().parse(new InputSource(new StringReader(inMemorySer))).getDocumentElement())) {
//                        logger.log(Level.INFO, "In-memory serialized XML doc - signurature verified!");
//                    } else
//                        logger.log(Level.SEVERE, "In-memory serialized XML doc - signurature NOT verified!");
//                    
//                    String indentedInMemorySer = XMLPrinter.printDocumentElement(auCapRevXMLDoc.getDocumentElement(), true);
//                    System.out.println("Indented Signed token\n" + indentedInMemorySer);
//                    if (X509DocumentSigner.verifyXMLElementSign(xmlDocBuilderFactory.newDocumentBuilder().parse(new InputSource(new StringReader(indentedInMemorySer))).getDocumentElement())) {
//                        logger.log(Level.INFO, "INDENTED In-memory serialized XML doc - signurature verified!");
//                    } else
//                        logger.log(Level.SEVERE, "INDENTED In-memory serialized XML doc - signurature NOT verified!");
                } catch (Exception ex) {
                    panelAssertion.setProgressValue(0);
                    panelAssertion.setProgressText("An error occurred: " + ex.getMessage());
                    logger.log(Level.INFO, ex.getMessage(), ex);
                }
            }
        };
        t.start();
    }

    @Override
    public boolean aboutToHidePanel() {
        return true;
    }

    public URI getActualTokenPath() throws PanelAssertionException {
        if (auCapRevXMLDocURI == null) {
            throw new PanelAssertionException("The Capability Revocation token was not saved.");
        } else {
            return auCapRevXMLDocURI;
        }
    }
    
    public Document getAccessRightsCapabilityToken () throws PanelAssertionException {
        if (auCapRevXMLDoc == null) {
            throw new PanelAssertionException("No capability revocation token saved.");
        } else {
            return auCapRevXMLDoc;
        }
    }

    public boolean saveSignedToken(File assertionFile) {
        if (auCapRevXMLDoc != null) {
            String directory = assertionFile.getParentFile().getAbsolutePath();
            String fileName = assertionFile.getName();
            FilterXML fa = new FilterXML("Capability Revocation");
            if (!fa.accept(assertionFile)) {
                fileName = fileName + "." + FilterXML.ASSERTION;
            }
            File saveFile = new File(directory, fileName);
            if (saveFile.exists()) {
                if (!getReplaceFileConfirmation()) {
                    fileSaved = false;
                    return false;
                }
            }
            try {
                FileWriter fileWriter = new FileWriter(saveFile.toURI().getPath());

                fileWriter.append(
                        XMLPrinter.printDocumentElement(
                        auCapRevXMLDoc.getDocumentElement(), false));

                fileWriter.close();
                
                //DEBUG
//                fileWriter = new FileWriter(saveFile.toURI().getPath() + "indented");
//
//                fileWriter.append(
//                        XMLPrinter.printDocumentElement(
//                        auCapRevXMLDoc.getDocumentElement(), true));
//
//                fileWriter.close();
//                
//                //DOM LEVEL 2
//                DocumentBuilderFactory xmlDocBuilderFactory = DocumentBuilderFactory.newInstance();
//                xmlDocBuilderFactory.setNamespaceAware(true);
//                xmlDocBuilderFactory.setIgnoringElementContentWhitespace(true);
//                try {
//                    DocumentBuilder xmlDocBuilder = xmlDocBuilderFactory.newDocumentBuilder();
//                    Document test1 = xmlDocBuilder.parse(new File (saveFile.toURI()));
////                    Document test1 = parser.parse(input);
//                    logger.log(Level.INFO, "Checking signature of file " + saveFile.getAbsolutePath());
//                    if (X509DocumentSigner.verifyXMLElementSign(test1.getDocumentElement())) {
//                        logger.log(Level.INFO, "Signature of file " + saveFile.getAbsolutePath() + " has been VERIFIED!");
//                    } else {
//                        logger.log(Level.INFO, "Signature of file " + saveFile.getAbsolutePath() + " is NOT VALID!");
//                    }
//                
//                    
//                    test1 = xmlDocBuilder.parse(new File (saveFile.toURI().getPath() + "indented"));
////                    input = domLevel3.createLSInput();
////                    input.setCharacterStream(new FileReader(new File (saveFile.toURI().getPath() + "indented")));
//                    logger.log(Level.INFO, "Checking signature of indented file " + saveFile.getAbsolutePath());
//                    if (X509DocumentSigner.verifyXMLElementSign(test1.getDocumentElement())) {
//                        logger.log(Level.INFO, "Signature of indented file " + saveFile.getAbsolutePath() + " has been VERIFIED!");
//                    } else {
//                        logger.log(Level.INFO, "Signature of indented file " + saveFile.getAbsolutePath() + " is NOT VALID!");
//                    }
//                    
//                    String fixedFile = "D:\\TMP\\prova13.xml";
//                    logger.log(Level.INFO, "Checking signature of file " + fixedFile);
//                    test1 = xmlDocBuilder.parse(new File (fixedFile));
//                    X509DocumentSigner.verifyXMLElementSign(test1.getDocumentElement());
//                } catch (ParserConfigurationException e) {
//                    e.printStackTrace();
//                } catch (SAXException e) {
//                    e.printStackTrace();
//                } catch (GeneralSecurityException e) {
//                    e.printStackTrace();
//                }
//                
                //END DELETE
 
                
                auCapRevXMLDocURI = saveFile.toURI();
                fileSaved = true;
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(getWizard(),
                        ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
//            } catch (ClassCastException ex) {
//                JOptionPane.showMessageDialog(getWizard(),
//                        ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
//            } catch (ClassNotFoundException ex) {
//                JOptionPane.showMessageDialog(getWizard(),
//                        ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
//            } catch (InstantiationException ex) {
//                JOptionPane.showMessageDialog(getWizard(),
//                        ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
//            } catch (IllegalAccessException ex) {
//                JOptionPane.showMessageDialog(getWizard(),
//                        ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
        
        return fileSaved;
    }

    private PanelAssertionObject recoverPaneAssertionObject(Object backwardObject) throws PanelAssertionException {
        if (backwardObject == null) {
            throw new PanelAssertionException("No revocation token component available.");
        }
        if (!(backwardObject instanceof PanelAssertionObject)) {
            throw new PanelAssertionException("Unexpected revocation token object.");
        }
        PanelAssertionObject pao = (PanelAssertionObject) backwardObject;
        if (pao.getRevocationType() == null) {
            throw new PanelAssertionException("Revocation data are null.");
        }
        if (pao.getKeyValues() == null) {
            throw new PanelAssertionException("Certificate's keys are null.");
        }
        return pao;
    }

    private boolean getReplaceFileConfirmation() {
        Object[] options = {"Yes", "No"};
        int resp = JOptionPane.showOptionDialog(getWizard(),
                "The revocation token already exists. Do you want to replace the existing file?",
                "Save Capability Revocation token",
                JOptionPane.YES_NO_OPTION,
                JOptionPane.QUESTION_MESSAGE,
                null,
                options,
                options[0]);
        return (resp == JOptionPane.YES_OPTION ? true : false);
    }
    
    public boolean isFileSaved () {
        return fileSaved;
    }
}
